﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
using WindowsPhoneEssentials.Diagnostics;
using WindowsPhoneMVC.Navigation;
using WindowsPhoneMVC.Phone.Interfaces;

namespace WindowsPhoneMVC.Phone
{
    /// <summary>
    /// Navigation frame is used to display content (action results)
    /// </summary>
    public class MvcFrame : ContentControl, IMvcFrame
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="Frame"/> class.
        /// </summary>
        public MvcFrame()
        {
            DefaultStyleKey = typeof(MvcFrame);
        }

        #region public bool IsNavigationInProgress
        public static readonly DependencyProperty IsNavigationInProgressProperty =
            DependencyProperty.Register("IsNavigationInProgress", typeof (bool), typeof (MvcFrame), new PropertyMetadata(false));

        public bool IsNavigationInProgress
        {
            get { return (bool) GetValue(IsNavigationInProgressProperty); }
            set { SetValue(IsNavigationInProgressProperty, value); }
        }
        #endregion

        #region public bool ShowProgressBar
        public static readonly DependencyProperty ShowProgressBarProperty =
            DependencyProperty.Register("ShowProgressBar", typeof (bool), typeof (MvcFrame), new PropertyMetadata(false));

        public bool ShowProgressBar
        {
            get { return (bool) GetValue(ShowProgressBarProperty); }
            set { SetValue(ShowProgressBarProperty, value); }
        }
        #endregion

        #region public Visibility ProgressBarVisibility
        public static readonly DependencyProperty ProgressBarVisibilityProperty =
            DependencyProperty.Register("ProgressBarVisibility", typeof (Visibility), typeof (MvcFrame), new PropertyMetadata(Visibility.Collapsed));

        public Visibility ProgressBarVisibility
        {
            get { return (Visibility) GetValue(ProgressBarVisibilityProperty); }
            set { SetValue(ProgressBarVisibilityProperty, value); }
        }
        #endregion

        #region public bool IsContentEnabled
        public static readonly DependencyProperty IsContentEnabledProperty =
            DependencyProperty.Register("IsContentEnabled", typeof (bool), typeof (MvcFrame), new PropertyMetadata(true));

        public bool IsContentEnabled
        {
            get { return (bool) GetValue(IsContentEnabledProperty); }
            set { SetValue(IsContentEnabledProperty, value); }
        }
        #endregion

        #region public string LoadingMessage
        public static readonly DependencyProperty LoadingMessageProperty =
            DependencyProperty.Register("LoadingMessage", typeof (string), typeof (MvcFrame), new PropertyMetadata("Loading..."));

        public string LoadingMessage
        {
            get { return (string) GetValue(LoadingMessageProperty); }
            set { SetValue(LoadingMessageProperty, value); }
        }
        #endregion

        #region public double ContentOpacity
        public static readonly DependencyProperty ContentOpacityProperty =
            DependencyProperty.Register("ContentOpacity", typeof (double), typeof (MvcFrame), new PropertyMetadata(1d));

        public double ContentOpacity
        {
            get { return (double) GetValue(ContentOpacityProperty); }
            set { SetValue(ContentOpacityProperty, value); }
        }
        #endregion


        protected IApplicationBar CurrentApplicationBar;
        protected readonly Dictionary<object, bool> AppBarEnabledLookup = new Dictionary<object, bool>();
        private ContentPresenter contentPresenter;
        private readonly object backgroundOperationLock = new object();
        private int currentBackgroundOperations;
        private Action<bool> setProgressIndicatorVisible;

        public virtual void SetApplicationBar(IApplicationBar applicationBar)
        {
            CurrentApplicationBar = applicationBar;
            Shell.SetApplicationBar(applicationBar);
        }

        public void Initialise(IShell shell)
        {
            Shell = shell;
        }

        public void UpdateOrientation(PhoneApplicationPage view)
        {
            Shell.SupportedOrientations = view.SupportedOrientations;
        }

        public void SetupSystemTray(PhoneApplicationPage page)
        {
            var target = (FrameworkElement)Shell;
            TransferSystemTrayToShell(SystemTray.BackgroundColorProperty, page, target);
            TransferSystemTrayToShell(SystemTray.ForegroundColorProperty, page, target);
            TransferSystemTrayToShell(SystemTray.IsVisibleProperty, page, target);
            TransferSystemTrayToShell(SystemTray.OpacityProperty, page, target);
#if WINDOWS_PHONE71
            var localValue = page.ReadLocalValue(SystemTray.ProgressIndicatorProperty);
            if (localValue != DependencyProperty.UnsetValue)
            {
                Trace.WriteWarning(typeof(MvcFrame), ()=> "View {0} overrides ProgressIndicator, MVC uses the ProgressIndicator for background navigations");
                setProgressIndicatorVisible = b => ((ProgressIndicator) localValue).IsVisible = b;
            }
            else
            {
                setProgressIndicatorVisible = b => progressIndicator.IsVisible = b;
                localValue = progressIndicator;
            }
            SystemTray.SetProgressIndicator(target, (ProgressIndicator)localValue);
#endif
        }

        public void BeginBackgroundOperation()
        {
            lock (backgroundOperationLock)
            {
                currentBackgroundOperations++;
                UpdateBackroundProgress();
            }
        }

        public void EndBackgroundOperation()
        {
            lock (backgroundOperationLock)
            {
                currentBackgroundOperations--;
                UpdateBackroundProgress();
            }
        }

        private void UpdateBackroundProgress()
        {
            setProgressIndicatorVisible(currentBackgroundOperations > 0);
        }

        private static void TransferSystemTrayToShell(DependencyProperty dependencyProperty, FrameworkElement page, FrameworkElement target)
        {
            var bindingExpression = page.GetBindingExpression(dependencyProperty);
            if (bindingExpression != null)
                target.SetBinding(dependencyProperty, bindingExpression.ParentBinding);
            else
            {
                var localValue = page.ReadLocalValue(dependencyProperty);
                if (localValue != DependencyProperty.UnsetValue)
                    target.SetValue(dependencyProperty, localValue);
            }
        }

#if !WINDOWS_PHONE71
        private WindowsPhoneMVC.Controls.PerformanceProgressBar progressIndicator;
#else
        private readonly ProgressIndicator progressIndicator = new ProgressIndicator
        {
            IsIndeterminate = true
        };
#endif

        /// <summary>
        /// When overridden in a derived class, is invoked whenever application code or internal processes (such as a rebuilding layout pass) call
        /// <see cref="M:System.Windows.Controls.Control.ApplyTemplate"/>.
        /// In simplest terms, this means the method is called just before a UI element displays in an application.
        /// </summary>
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            contentPresenter = GetTemplateChild("PART_MainContent") as ContentPresenter;
#if !WINDOWS_PHONE71
            progressIndicator = GetTemplateChild("PART_ProgressIndicator") as PerformanceProgressBar;
            setProgressIndicatorVisible = b=>
            {
                if (b)
                {
                    progressIndicator.IsLoading = true;
                    progressIndicator.Visibility = Visibility.Visible;
                }
                else
                {
                    progressIndicator.IsLoading = false;
                    progressIndicator.Visibility = Visibility.Collapsed;
                }
            }
#endif
        }

        /// <summary>
        /// Called when navigation is started.
        /// </summary>
        /// <param name="direction"></param>
        public virtual void OnNavigationStarted(NavigationDirection direction)
        {
            if (IsNavigationInProgress)
                throw new InvalidOperationException("MvcFrame is already navigating");

            if (contentPresenter != null)
                contentPresenter.CacheMode = new BitmapCache();

            NavigationDirection = direction;
            IsContentEnabled = false;
            ContentOpacity = 0.2;
            IsNavigationInProgress = true;
            ShowProgressBar = true;
            ProgressBarVisibility = Visibility.Visible;
            LoadingMessage = null;

            if (CurrentApplicationBar == null) return;

            foreach (ApplicationBarIconButton button in CurrentApplicationBar.Buttons)
            {
                AppBarEnabledLookup.Add(button, button.IsEnabled);
                button.IsEnabled = false;
            }

            foreach (ApplicationBarMenuItem menuItem in CurrentApplicationBar.MenuItems)
            {
                AppBarEnabledLookup.Add(menuItem, menuItem.IsEnabled);
                menuItem.IsEnabled = false;
            }
        }

        public NavigationDirection NavigationDirection { get; private set; }

        protected IShell Shell { get; set; }

        /// <summary>
        /// Called when navigation is completed.
        /// </summary>
        public virtual void OnNavigationCompleted(bool successful)
        {
            IsNavigationInProgress = false;
            IsContentEnabled = true;

            if (ShowProgressBar) ShowProgressBar = false;
            if (ProgressBarVisibility == Visibility.Visible) ProgressBarVisibility = Visibility.Collapsed;
            ContentOpacity = 1;

            if (contentPresenter != null)
                contentPresenter.CacheMode = null;

            if (CurrentApplicationBar != null)
            {
                foreach (var button in CurrentApplicationBar.Buttons.Cast<ApplicationBarIconButton>().Where(button => AppBarEnabledLookup.ContainsKey(button)))
                {
                    button.IsEnabled = AppBarEnabledLookup[button];
                }

                foreach (var menuItem in CurrentApplicationBar.MenuItems.Cast<ApplicationBarMenuItem>().Where(menuItem => AppBarEnabledLookup.ContainsKey(menuItem)))
                {
                    menuItem.IsEnabled = AppBarEnabledLookup[menuItem];
                }
            }
            AppBarEnabledLookup.Clear();
        }

        /// <summary>
        /// Shows specified view in this navigation frame.
        /// </summary>
        /// <param name="view">View.</param>
        public virtual void Show(PhoneApplicationPage view)
        {
            if (!IsNavigationInProgress)
                throw new InvalidOperationException("MvcFrame must be navigating to show new content");

            ShowProgressBar = false;
            ProgressBarVisibility = Visibility.Collapsed;
            ContentOpacity = 1;
            Content = view;
        }
    }
}
